﻿#include "SGRHelper.h"
#include "global.h"
#include "debug.h"
#include "Command.h"
#include "Disk.h"
#include "Test.h"
#include "Exception.h"
#include "DiskManager.h"
#include "FileSystem.h"
#include "INode.h"

#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <string>
#include <format>
#include <filesystem>

using namespace std;

namespace PO = boost::program_options;
namespace FS = std::filesystem;

Command cmdNew;		// 新建磁盘命令
Command cmdSfs;		// 打开文件系统命令
Command cmdQuit;	// 退出命令
Command cmdHelp;	// 帮助命令
Command cmdInfo;	// 信息命令
Command cmdPwd;		// 打印当前目录命令

// 打印模拟器信息
static void printInfo()
{
	Tool::printInfo(Const::SIMULATOR_NAME, Const::SIMULATOR_DESCRIPTION, Const::SIMULATOR_INFO);
}

// 打印帮助信息
static void printHelp()
{
	Tool::printHelp(Const::SIMULATOR_COMMAND_INFO);
}

// 打印退出信息
static void printQuitInfo()
{
	cout << Const::ATTR_INFO << "Simulator is now quiting..." << endl;
	cout << "Thank you for using " << Const::SIMULATOR_NAME << ", Goodbye!" << SGR::Reset << endl;
}

// 构建新建磁盘命令
void buildCmdNew()
{
	cmdNew.name = "new";
	cmdNew.format = "disks [options]";
	cmdNew.description = Const::SIMULATOR_COMMAND_INFO.at("new");

	PO::options_description *desc = new PO::options_description("Allowed options", Const::SIMLUATOR_COMMAND_HELP_WIDTH);
	desc->add_options()
		("disks,d", PO::value<vector<string>>()->multitoken(), "The paths of disk files. Accept multiple files. If file is not ending with .disk, .disk will be appended.")
		("silent,s", "Do not print extra information.")
		("force,f", "Force overwrite exist disk files.")
		("ignore,i", "Ignore exist disk files.")
		("block,b", PO::value<uint64_t>()->notifier(([](uint64_t v) {
			if(v < Const::MINIMUM_MAX_DISK_PAGE) throw Exception(format("The maximum block count of each disk must be >= {}.", Const::MINIMUM_MAX_DISK_PAGE));
		}))->default_value(Const::DEFAULT_MAX_DISK_PAGE), format("The maximum block count of each disk. Must be >= {}. Default is {}. Then disk file won't be full size at first, it will expand automatically while using, until reach the maximum block count.", Const::MINIMUM_MAX_DISK_PAGE, Const::DEFAULT_MAX_DISK_PAGE).c_str())
		("help,h", Const::DEFAULT_HELP_MESSAGE)
		;
	cmdNew.setOptDesc(desc);

	PO::positional_options_description *posDesc = new PO::positional_options_description();
	posDesc->add("disks", -1);
	cmdNew.setPosDesc(posDesc);
}

// 构建打开文件系统命令
void buildCmdSfs()
{
	cmdSfs.name = "sfs";
	cmdSfs.format = "sysdisk [options]";
	cmdSfs.description = Const::SIMULATOR_COMMAND_INFO.at("sfs");

	PO::options_description *desc = new PO::options_description("Allowed options", Const::SIMLUATOR_COMMAND_HELP_WIDTH);
	desc->add_options()
		("sysdisk,S", PO::value<string>(), "The path of system disk file. Accept only one file. If file is not ending with .disk, .disk will be appended.")
		("ignore,i", "Ignore not exist disk files.")
		("silent,s", "Do not print extra information.")
		("disks,d", PO::value<vector<string>>()->multitoken()->notifier([](vector<string> v) {
			if (v.size() > 23) throw Exception("The number of disk files must be less than 23.");
		}), "The paths of disk files (less than 23). Accept multiple files. If file is not ending with .disk, .disk will be appended.")
		("help,h", Const::DEFAULT_HELP_MESSAGE)
		;
	cmdSfs.setOptDesc(desc);

	PO::positional_options_description *posDesc = new PO::positional_options_description();
	posDesc->add("sysdisk", 1);
	cmdSfs.setPosDesc(posDesc);
}

// 构建推出命令
void buildCmdQuit()
{
	cmdQuit.name = "quit";
	cmdQuit.format = "[options]";
	cmdQuit.description = Const::SIMULATOR_COMMAND_INFO.at("quit");

	PO::options_description *desc = new PO::options_description("Allowed options", Const::SIMLUATOR_COMMAND_HELP_WIDTH);
	desc->add_options()
		("force,f", "Quit without asking for confirmation.")
		("silent,s", "Quit without printing quit information.")
		("help,h", Const::DEFAULT_HELP_MESSAGE)
		;

	cmdQuit.setOptDesc(desc);
}

// 构建帮助命令
void buildCmdHelp()
{
	cmdHelp.name = "help";
	cmdHelp.format = "[options]";
	cmdHelp.description = Const::SIMULATOR_COMMAND_INFO.at("help");

	PO::options_description *desc = new PO::options_description("Allowed options", Const::SIMLUATOR_COMMAND_HELP_WIDTH);
	desc->add_options()
		("help,h", Const::DEFAULT_HELP_MESSAGE)
		;
	cmdHelp.setOptDesc(desc);
}

// 构建信息命令
void buildCmdInfo()
{
	cmdInfo.name = "info";
	cmdInfo.format = "[options]";
	cmdInfo.description = Const::SIMULATOR_COMMAND_INFO.at("info");

	PO::options_description *desc = new PO::options_description("Allowed options", Const::SIMLUATOR_COMMAND_HELP_WIDTH);
	desc->add_options()
		("help,h", Const::DEFAULT_HELP_MESSAGE)
		;
	cmdInfo.setOptDesc(desc);
}

// 构建当前路径命令
void buildCmdPwd()
{
	cmdPwd.name = "pwd";
	cmdPwd.format = "[options]";
	cmdPwd.description = Const::SIMULATOR_COMMAND_INFO.at("pwd");

	PO::options_description *desc = new PO::options_description("Allowed options", Const::SIMLUATOR_COMMAND_HELP_WIDTH);
	desc->add_options()
		("help,h", Const::DEFAULT_HELP_MESSAGE)
		;
	cmdPwd.setOptDesc(desc);
}

// 执行新建磁盘命令
void execCmdNew()
{
	if (cmdNew.has("help")) {
		cmdNew.printHelp();
		return;
	}

	if (!cmdNew.has("disks")) {
		Tool::printError("disks (Disk files) is required.");
		Tool::printNotice("Use " + Const::ATTR_COMMAND_NAME.decorate("-h") + " or " + Const::ATTR_COMMAND_NAME.decorate("--help") + " for help.");
		return;
	}

	// 解析命令参数
	vector<string> diskNames = cmdNew["disks"].as<vector<string>>();
	debug(diskNames);

	vector<FS::path> diskPaths;
	for (const string &name : diskNames) {
		FS::path filePath = FS::path(name);
		if (filePath.extension().string() != Const::DISK_FILE_SUFFIX) {
			filePath += Const::DISK_FILE_SUFFIX;
		}
		diskPaths.push_back(filePath);
	}
	debug(diskPaths)

	vector<FS::path> existDiskPaths;
	for (const FS::path &path : diskPaths) {
		if (FS::exists(path)) {
			existDiskPaths.push_back(path);
		}
	}

	bool isForce = cmdNew.has("force");
	bool isSilent = cmdNew.has("silent");
	bool isIgnore = cmdNew.has("ignore");

	// 如果存在磁盘文件，则询问是否覆盖
	if(!existDiskPaths.empty() && !isForce) {
		bool result = false;
		if(!isIgnore) {
			string msg = "";
			for (const FS::path &path : existDiskPaths) {
				msg += "\"" + path.string() + "\" ";
			}
			msg += "already exist. Overwrite?";
			result = Tool::getUserConfirm(msg);
		}
		
		if (!result) {
			if(!isIgnore) {
				if(!Tool::getUserConfirm("Ignore exist disk files(y) or cancel operation(n)?")) {
					cout << "Operation canceled." << endl;
					return;
				}
			}

			for (const FS::path &path : existDiskPaths) {
				diskPaths.erase(find(diskPaths.begin(), diskPaths.end(), path));
			}

			if (!diskPaths.empty() && !isSilent) {
				Tool::printInfo(Const::ATTR_INFO.decorate("Remaining disk files: ") + Tool::buildPathsString(diskPaths));
			}
		}
	}

	if (diskPaths.empty() && !isSilent) {
		Tool::printInfo("No disk files need to be created, operation canceled.");
		return;
	}

	// 获取最大磁盘块数参数
	uint64_t maxDiskBlockCount = cmdNew["block"].as<uint64_t>();

	// 创建磁盘
	for(const FS::path &path : diskPaths) {
		try {
			DiskManager diskManager;
			diskManager.create(path, maxDiskBlockCount);
			Tool::printSuccess(std::format("Disk {} created.", path.string()));
		} catch (exception &e) {
			Tool::printError(std::format("Error when creating disk {}. Error message: {}", path.string(), string(e.what())));
		}
	}
}

// 执行打开文件系统命令
void execCmdSfs()
{
	if (cmdSfs.has("help")) {
		cmdSfs.printHelp();
		return;
	}

	if (!cmdSfs.has("sysdisk")) {
		Tool::printError(Const::ATTR_COMMAND_NAME.decorate("sysdisk") + " (System disk file) is required.");
		Tool::printNotice("Use " + Const::ATTR_COMMAND_NAME.decorate("-h") + " or " + Const::ATTR_COMMAND_NAME.decorate("--help") + " for help.");
		return;
	}

	// 解析命令参数
	string sysFileName = cmdSfs["sysdisk"].as<string>();
	debug(sysFileName);

	vector<string> diskNames;
	if (cmdSfs.has("disks")) {
		diskNames = cmdSfs["disks"].as<vector<string>>();
	}
	debug(diskNames);

	FS::path sysFilePath = FS::path(sysFileName);
	if (sysFilePath.extension().string() != Const::DISK_FILE_SUFFIX) {
		sysFilePath += Const::DISK_FILE_SUFFIX;
	}
	debug(sysFilePath);

	vector<FS::path> diskPaths;
	for (const string &name : diskNames) {
		FS::path filePath = FS::path(name);
		if (filePath.extension().string() != Const::DISK_FILE_SUFFIX) {
			filePath += Const::DISK_FILE_SUFFIX;
		}
		diskPaths.push_back(filePath);
	}
	debug(diskPaths);

	if(!FS::exists(sysFilePath)) {
		Tool::printError("System disk file \"" + sysFilePath.string() + "\" does not exist.");
		return;
	}

	vector<FS::path> notExistPaths;
	for (const FS::path &diskPath : diskPaths) {
		if (!FS::exists(diskPath)) {
			notExistPaths.push_back(diskPath);
		}
	}

	bool isSilent = cmdSfs.has("silent");
	bool isIgnore = cmdSfs.has("ignore");

	// 如果打开的文件系统不存在，询问是否忽略
	if (!notExistPaths.empty()) {
		if(!isIgnore) {
			string msg = "";
			for (const FS::path &path : notExistPaths) {
				msg += "\"" + path.string() + "\" ";
			}
			msg += "does not exist. Ignore not exist files(y) or cancel operation(n)?";
			if(!Tool::getUserConfirm(msg)) {
				cout << "Operation canceled." << endl;
				return;
			}
		}
		
		for (const FS::path &path : notExistPaths) {
			diskPaths.erase(find(diskPaths.begin(), diskPaths.end(), path));
		}

		if (!diskPaths.empty() && !isSilent) {
			Tool::printInfo(Const::ATTR_INFO.decorate("Remaining disk files: ") + Tool::buildPathsString(diskPaths));
		}
	}

	if (diskPaths.empty() && !isSilent) {
		Tool::printInfo("System will be executed without extra disks.");
	}

	if (!isSilent) {
		Tool::printInfo("Executing system...");
	}

	// 执行文件系统
	FileSystem fileSystem;
	try {
		fileSystem.exec(sysFilePath, diskPaths);
	} catch (exception &e) {
		Tool::printError(std::format("Error when executing system {}. Error message: {}", sysFilePath, string(e.what())));
		return;
	}
}

// 执行退出命令，返回是否退出
bool execCmdQuit()
{
	if (cmdQuit.has("help")) {
		cmdQuit.printHelp();
		return false;
	}

	bool isForce = cmdQuit.has("force");
	bool isSilent = cmdQuit.has("silent");

	if (!isForce && !Tool::getUserConfirm("Are you sure you want to quit?")) {
		return false;
	}

	if (!isSilent) {
		printQuitInfo();
	}
	return true;
}

// 执行帮助命令
void execCmdHelp()
{
	if (cmdHelp.has("help")) {
		cmdHelp.printHelp();
		return;
	}

	printHelp();
}

// 执行信息命令
void execCmdInfo()
{
	if (cmdInfo.has("help")) {
		cmdInfo.printHelp();
		return;
	}

	printInfo();
}

// 执行当前路径命令
void execCmdPwd()
{
	if (cmdPwd.has("help")) {
		cmdPwd.printHelp();
		return;
	}

	cout << SGR::Bold << Const::SIMULATOR_PWD << SGR::Reset << endl;
}

int main()
{
#ifdef _DEBUG
	debug_assert(sizeof(INode) == INode::INODE_SIZE);

	// Test::test_disk_create_open_close();
	// Test::test_disk_read_write();
	// Test::test_disk_manager_create_open_close();
	// Test::test_disk_manager_page_alloc_free();
	// Test::test_disk_manager_inode_alloc_free();
	// Test::test_disk_manager_set_get_inode();
	// Test::test_disk_manager_read_write();
	// Test::test_dirfile_manager_append_readall_query();
	// Test::test_dirfile_manager_find_exist();
	// Test::test_dirfile_manager_swap_popback();
	// Test::test_dirfile_manager_update_removeall();
	// cerr << endl;
#endif

	// 注册命令
	buildCmdNew();
	buildCmdSfs();
	buildCmdQuit();
	buildCmdHelp();
	buildCmdInfo();
	buildCmdPwd();

	// 打印欢迎信息
	cout << Const::ATTR_INFO << "Welcome to " << Const::SIMULATOR_NAME << "!" << SGR::Reset << endl;
	cout << endl;

	printInfo();
	cout << endl;

	printHelp();
	cout << endl;

	// 打印命令提示
	for (string s; ; cout << endl) {
		string command;
		stringstream ss;

		// 获取命令
		while (command.empty()) {
			// 打印命令提示符
			cout << Const::ATTR_SIMULATOR_TERMINAL_NAME << Const::SIMULATOR_TERNIMAL_NAME << SGR::Reset;
			cout << Const::ATTR_TERMINAL_SPLITTER << Const::SIMULATOR_TERMINAL_SPLITTER << SGR::Reset;
			cout << " " << flush;

			getline(cin, s);
			debug(s);

			stringstream ss(s);
			ss >> command;
			debug(command);
		}

		try {
			if (command == "new") {
				if (!cmdNew.parse(s)) {
					continue;
				}

				execCmdNew();
			} else if (command == "sfs") {
				if (!cmdSfs.parse(s)) {
					continue;
				}

				execCmdSfs();
			} else if (command == "quit") {
				if (!cmdQuit.parse(s)) {
					continue;
				}

				if (execCmdQuit()) {
					break;
				}
			} else if (command == "help") {
				if (!cmdHelp.parse(s)) {
					continue;
				}

				execCmdHelp();
			} else if (command == "info") {
				if (!cmdInfo.parse(s)) {
					continue;
				}

				execCmdInfo();
			} else if (command == "pwd") {
				if (!cmdPwd.parse(s)) {
					continue;
				}

				execCmdPwd();
			} else {
				string msg;
				msg = "Unknown command: " + command + ". Use " + Const::ATTR_COMMAND_NAME.decorate("help") + " for help.";
				Tool::printError(msg);
			}
		} catch (exception &e) {
			Tool::printError(std::format("Error when executing command. Error message: {}", string(e.what())));
		}
	}

	return 0;
}
